home *** CD-ROM | disk | FTP | other *** search
/ Shareware Super Platinum 8 / Shareware Super Platinum 8.iso / mac / WIN_PRO / DS-1.ZIP;1 / PREPROC.ZIP / BLDTOK.C next >
Encoding:
C/C++ Source or Header  |  1992-02-10  |  21.6 KB  |  768 lines

  1. /*
  2.  * This file contains routines for building tokens out of characters from a
  3.  *  "character source". This source is the top element on the source stack.
  4.  */
  5. #include "../preproc/preproc.h"
  6. #include <ctype.h>
  7.  
  8. /*
  9.  * Prototypes for static functions.
  10.  */
  11. hidden int           pp_tok_id  Params((char *s));
  12. hidden struct token *chck_wh_sp Params((struct char_src *cs));
  13. hidden struct token *pp_number  Params((noargs));
  14. hidden struct token *char_str   Params((int delim, int tok_id));
  15. hidden struct token *hdr_tok    Params((int delim, int tok_id,
  16.                                   struct char_src *cs));
  17.  
  18. int whsp_image = NoSpelling;    /* indicate what is in white space tokens */
  19. struct token *zero_tok;         /* token for literal 0 */
  20. struct token *one_tok;          /* token for literal 1 */
  21.  
  22. #include "../preproc/pproto.h"
  23.  
  24. /*
  25.  * IsWhSp(c) - true if c is a white space character.
  26.  */
  27. #define IsWhSp(c) (c == ' ' || c == '\n' || c == '\t' || c == '\v' || c == '\f')
  28.  
  29. /*
  30.  * AdvChar() - advance to next charactor from buffer, filling the buffer
  31.  *   if needed.
  32.  */
  33. #define AdvChar() \
  34.    if (++next_char == last_char) \
  35.       fill_cbuf();
  36.  
  37. static int line;                   /* current line number */
  38. static char *fname;                /* current file name */
  39. static struct str_buf tknize_sbuf; /* string buffer */
  40.  
  41. /*
  42.  * List of preprocessing directives and the corresponding token ids.
  43.  */
  44. static struct rsrvd_wrd pp_rsrvd[] = {
  45.    PPDirectives
  46.    {"if",      PpIf},
  47.    {"else",    PpElse},
  48.    {"ifdef",   PpIfdef},
  49.    {"ifndef",  PpIfndef},
  50.    {"elif",    PpElif},
  51.    {"endif",   PpEndif},
  52.    {"include", PpInclude},
  53.    {"define",  PpDefine},
  54.    {"undef",   PpUndef},
  55.    {"begdef",  PpBegdef},
  56.    {"enddef",  PpEnddef},
  57.    {"line",    PpLine},
  58.    {"error",   PpError},
  59.    {"pragma",  PpPragma},
  60.    {NULL, Invalid}};
  61.  
  62. /*
  63.  * init_tok - initialize tokenizer.
  64.  */
  65. novalue init_tok()
  66.    {
  67.    struct rsrvd_wrd *rw;
  68.    static int first_time = 1;
  69.  
  70.    if (first_time) {
  71.       first_time = 0;
  72.       init_sbuf(&tknize_sbuf); /* initialize string buffer */
  73.       /*
  74.        * install reserved words into the string table
  75.        */
  76.       for (rw = pp_rsrvd; rw->s != NULL; ++rw)
  77.          rw->s = spec_str(rw->s);
  78.  
  79.       zero_tok = new_token(PpNumber, spec_str("0"), "", 0);
  80.       one_tok = new_token(PpNumber, spec_str("1"), "", 0);
  81.       }
  82.    }
  83.  
  84. /*
  85.  * pp_tok_id - see if s in the name of a preprocessing directive.
  86.  */
  87. static int pp_tok_id(s)
  88. char *s;
  89.    {
  90.    struct rsrvd_wrd *rw;
  91.  
  92.    for (rw = pp_rsrvd; rw->s != NULL && rw->s != s; ++rw)
  93.       ;
  94.    return rw->tok_id;
  95.    }
  96.  
  97. /*
  98.  * chk_eq_sign - look ahead to next character to see if it is an equal sign.
  99.  *  It is used for processing -D options.
  100.  */
  101. int chk_eq_sign()
  102.    {
  103.    if (*next_char == '=') {
  104.       AdvChar();
  105.       return 1;
  106.       }
  107.    else
  108.       return 0;
  109.    }
  110.  
  111. /*
  112.  * chck_wh_sp - If the input is at white space, construct a white space token
  113.  *  and return it, otherwise return NULL. This function also helps keeps track
  114.  *  of preprocessor directive boundries.
  115.  */
  116. static struct token *chck_wh_sp(cs)
  117. struct char_src *cs;
  118.    {
  119.    register int c1, c2;
  120.    struct token *t;
  121.    int tok_id;
  122.  
  123.    /*
  124.     * See if we are at white space or a comment.
  125.     */
  126.    c1 = *next_char;
  127.    if (!IsWhSp(c1) && (c1 != '/' || next_char[1] != '*'))
  128.       return NULL;
  129.  
  130.    /*
  131.     * Fine the line number of the current character in the line number
  132.     *  buffer, and correct it if we have encountered any #line directives.
  133.     */
  134.    line = cs->line_buf[next_char - first_char] + cs->line_adj;
  135.    if (c1 == '\n')
  136.       --line;      /* a new-line really belongs to the previous line */
  137.  
  138.    tok_id = WhiteSpace;
  139.    for (;;) {
  140.       if (IsWhSp(c1)) {
  141.          /*
  142.           * The next character is a white space. If we are retaining the
  143.           *  image of the white space in the token, copy the character to
  144.           *  the string buffer. If we are in the midst of a preprocessor
  145.           *  directive and find a new-line, indicate the end of the
  146.           *  the directive.
  147.           */
  148.          AdvChar();
  149.          if (whsp_image != NoSpelling)
  150.             AppChar(tknize_sbuf, c1);
  151.          if (c1 == '\n') {
  152.             if (cs->dir_state == Within)
  153.                tok_id = PpDirEnd;
  154.             cs->dir_state = CanStart;
  155.             if (tok_id == PpDirEnd)
  156.                break;
  157.             }
  158.          }
  159.       else if (c1 == '/' && next_char[1] == '*') {
  160.          /*
  161.           * Start of comment. If we are retaining the image of comments,
  162.           *  copy the characters into the string buffer.
  163.           */
  164.          if (whsp_image == FullImage) {
  165.             AppChar(tknize_sbuf, '/');
  166.             AppChar(tknize_sbuf, '*');
  167.             }
  168.          AdvChar();
  169.          AdvChar();
  170.  
  171.          /*
  172.           * Look for the end of the comment.
  173.           */
  174.          c1 = *next_char;
  175.          c2 = next_char[1];
  176.          while (c1 != '*' || c2 != '/') {
  177.             if (c1 == EOF)
  178.                 errfl1(fname, line, "eof encountered in comment");
  179.             AdvChar();
  180.             if (whsp_image == FullImage)
  181.                AppChar(tknize_sbuf, c1);
  182.             c1 = c2;
  183.             c2 = next_char[1];
  184.             }
  185.  
  186.          /*
  187.           * Determine if we are retaining the image of a comment, replacing
  188.           *  a comment by one space character, or ignoring comments.
  189.           */
  190.          if (whsp_image == FullImage) {
  191.             AppChar(tknize_sbuf, '*');
  192.             AppChar(tknize_sbuf, '/');
  193.             }
  194.          else if (whsp_image == NoComment)
  195.             AppChar(tknize_sbuf, ' ');
  196.          AdvChar();
  197.          AdvChar();
  198.          }
  199.       else
  200.          break;         /* end of white space */
  201.       c1 = *next_char;
  202.       }
  203.  
  204.    /*
  205.     * If we are not retaining the image of white space, replace it all
  206.     *  with one space character.
  207.     */
  208.    if (whsp_image == NoSpelling)
  209.       AppChar(tknize_sbuf, ' ');
  210.  
  211.    t = new_token(tok_id, str_install(&tknize_sbuf), fname, line);
  212.  
  213.    /*
  214.     * Look ahead to see if a ## operator is next.
  215.     */
  216.    if (*next_char == '#' && next_char[1] == '#')
  217.       if (tok_id == PpDirEnd)
  218.          errt1(t, "## expressions must not cross directive boundries");
  219.       else {
  220.          /*
  221.           * Discard white space before a ## operator.
  222.           */
  223.          free_t(t);
  224.          return NULL;
  225.          }
  226.    return t;
  227.    }
  228.  
  229. /*
  230.  * pp_number - Create a token for a preprocessing number (See ANSI C Standard
  231.  *  for the syntax of such a number).
  232.  */
  233. static struct token *pp_number()
  234.    {
  235.    register int c;
  236.  
  237.    c = *next_char;
  238.    for (;;) {
  239.       if (c == 'e' || c == 'E') {
  240.          AppChar(tknize_sbuf, c);
  241.          AdvChar();
  242.          c = *next_char;
  243.          if (c == '+' || c == '-') {
  244.             AppChar(tknize_sbuf, c)
  245.             AdvChar();
  246.             c = *next_char;
  247.             }
  248.          }
  249.       else if (isdigit(c) || c == '.' || islower(c) || isupper(c) || c == '_') {
  250.          AppChar(tknize_sbuf, c);
  251.          AdvChar();
  252.          c = *next_char;
  253.          }
  254.       else {
  255.          return new_token(PpNumber, str_install(&tknize_sbuf), fname, line);
  256.          }
  257.       }
  258.    }
  259.  
  260. /*
  261.  * char_str - construct a token for a character constant or string literal.
  262.  */
  263. static struct token *char_str(delim, tok_id)
  264. int delim;
  265. int tok_id;
  266.    {
  267.    register int c;
  268.  
  269.    for (c = *next_char; c != EOF && c != '\n' &&  c != delim; c = *next_char) {
  270.       AppChar(tknize_sbuf, c);
  271.       if (c == '\\') {
  272.          c = next_char[1];
  273.          if (c == EOF || c == '\n')
  274.             break;
  275.          else {
  276.             AppChar(tknize_sbuf, c);
  277.             AdvChar();
  278.             }
  279.          }
  280.       AdvChar();
  281.       }
  282.    if (c == EOF)
  283.       errfl1(fname, line, "End-of-file encountered within a literal");
  284.    if (c == '\n')
  285.       errfl1(fname, line, "New-line encountered within a literal");
  286.    AdvChar();
  287.    return new_token(tok_id, str_install(&tknize_sbuf), fname, line);
  288.    }
  289.  
  290. /*
  291.  * hdr_tok - create a token for an #include header. The delimiter may be
  292.  *  > or ".
  293.  */
  294. static struct token *hdr_tok(delim, tok_id, cs)
  295. int delim;
  296. int tok_id;
  297. struct char_src *cs;
  298.    {
  299.    register int c;
  300.  
  301.    line = cs->line_buf[next_char - first_char] + cs->line_adj;
  302.    AdvChar();
  303.  
  304.    for (c = *next_char; c != delim; c = *next_char) {
  305.       if (c == EOF)
  306.          errfl1(fname, line,
  307.             "End-of-file encountered within a header name");
  308.       if (c == '\n')
  309.          errfl1(fname, line,
  310.             "New-line encountered within a header name");
  311.       AppChar(tknize_sbuf, c);
  312.       AdvChar();
  313.       }
  314.    AdvChar();
  315.    return new_token(tok_id, str_install(&tknize_sbuf), fname, line);
  316.    }
  317.  
  318. /*
  319.  * tokenize - return the next token from the character source on the top
  320.  *  of the source stack.
  321.  */
  322. struct token *tokenize()
  323.    {
  324.    struct char_src *cs;
  325.    struct token *t1, *t2;
  326.    register int c;
  327.    int tok_id;
  328.  
  329.  
  330.    cs = src_stack->u.cs;
  331.  
  332.    /*
  333.     * Check to see if the last call left a token from a look ahead.
  334.     */
  335.    if (cs->tok_sav != NULL) {
  336.       t1 = cs->tok_sav;
  337.       cs->tok_sav = NULL;
  338.       return t1;
  339.       }
  340.  
  341.    if (*next_char == EOF)
  342.       return NULL;
  343.  
  344.    /*
  345.     * Find the current line number and file name for the character
  346.     *  source and check for white space.
  347.     */
  348.    line = cs->line_buf[next_char - first_char] + cs->line_adj;
  349.    fname = cs->fname;
  350.    if ((t1 = chck_wh_sp(cs)) != NULL)
  351.        return t1;
  352.  
  353.    c = *next_char;  /* look at next character */
  354.    AdvChar();
  355.  
  356.    /*
  357.     * If the last thing we saw in this character source was white space
  358.     *  containing a new-line, then we must look for the start of a
  359.     *  preprocessing directive.
  360.     */
  361.    if (cs->dir_state == CanStart) {
  362.       cs->dir_state = Reset;
  363.       if  (c == '#' && *next_char != '#') {
  364.          /*
  365.           * Assume we are within a preprocessing directive and check
  366.           *  for white space to discard.
  367.           */
  368.          cs->dir_state = Within;
  369.          if ((t1 = chck_wh_sp(cs)) != NULL)
  370.             if (t1->tok_id == PpDirEnd) {
  371.                /*
  372.                 * We found a new-line, this is a null preprocessor directive.
  373.                 */
  374.                cs->tok_sav = t1;
  375.                AppChar(tknize_sbuf, '#');
  376.                return new_token(PpNull, str_install(&tknize_sbuf), fname, line);
  377.                }
  378.             else
  379.                free_t(t1);  /* discard white space */
  380.          c = *next_char;
  381.          if (islower(c) || isupper(c) || c == '_') {
  382.             /*
  383.              * Tokenize the identifier following the #
  384.              */
  385.             t1 = tokenize();
  386.             if ((tok_id = pp_tok_id(t1->image)) == Invalid) {
  387.                /*
  388.                 * We have a stringizing operation, not a preprocessing
  389.                 *  directive.
  390.                 */
  391.                cs->dir_state = Reset;
  392.                cs->tok_sav = t1;
  393.                AppChar(tknize_sbuf, '#');
  394.                return new_token('#', str_install(&tknize_sbuf), fname, line);
  395.                }
  396.             else {
  397.                t1->tok_id = tok_id;
  398.                if (tok_id == PpInclude) {
  399.                   /*
  400.                    * A header name has to be tokenized specially. Find
  401.                    *  it, then save the token.
  402.                    */
  403.                   if ((t2 = chck_wh_sp(cs)) != NULL)
  404.                      if (t2->tok_id == PpDirEnd)
  405.                         errt1(t2, "file name missing from #include");
  406.                      else
  407.                         free_t(t2);
  408.                   c = *next_char;
  409.                   if (c == '"')
  410.                      cs->tok_sav = hdr_tok('"', StrLit, cs);
  411.                   else if (c == '<')
  412.                      cs->tok_sav = hdr_tok('>', PpHeader, cs);
  413.                   }
  414.                /*
  415.                 * Return the token indicating the kind of preprocessor
  416.                 *  directive we have started.
  417.                 */
  418.                return t1;
  419.                }
  420.             }
  421.          else
  422.             errfl1(fname, line,
  423.                "# must be followed by an identifer or keyword");
  424.          }
  425.       }
  426.  
  427.    /*
  428.     * Check for literals containing wide characters.
  429.     */
  430.    if (c == 'L') {
  431.       if (*next_char == '\'') {
  432.          AdvChar();
  433.          t1 = char_str('\'', LCharConst);
  434.          if (t1->image[0] == '\0')
  435.             errt1(t1, "invalid character constant");
  436.          return t1;
  437.          }
  438.       else if (*next_char == '"') {
  439.          AdvChar();
  440.          return char_str('"', LStrLit);
  441.          }
  442.       }
  443.  
  444.    /*
  445.     * Check for identifier.
  446.     */
  447.    if (islower(c) || isupper(c) || c == '_') {
  448.       AppChar(tknize_sbuf, c);
  449.       c = *next_char;
  450.       while (islower(c) || isupper(c) || isdigit(c) || c == '_') {
  451.          AppChar(tknize_sbuf, c);
  452.          AdvChar();
  453.          c = *next_char;
  454.          }
  455.       return new_token(Identifier, str_install(&tknize_sbuf), fname, line);
  456.      }
  457.  
  458.    /*
  459.     * Check for number.
  460.     */
  461.    if (isdigit(c)) {
  462.       AppChar(tknize_sbuf, c);
  463.       return pp_number();
  464.       }
  465.   
  466.    /*
  467.     * Check for character constant.
  468.     */
  469.    if (c == '\'') {
  470.       t1 = char_str(c, CharConst);
  471.       if (t1->image[0] == '\0')
  472.          errt1(t1, "invalid character constant");
  473.       return t1;
  474.       }
  475.  
  476.    /*
  477.     * Check for string constant.
  478.     */
  479.    if (c == '"')
  480.       return char_str(c, StrLit);
  481.  
  482.    /*
  483.     * Check for operators and punctuation. Anything that does not fit these
  484.     *  categories is a single character token.
  485.     */
  486.    AppChar(tknize_sbuf, c)
  487.    switch (c) {
  488.       case '.':
  489.          c = *next_char;
  490.          if (isdigit(c)) {
  491.             /*
  492.              * Number
  493.              */
  494.             AppChar(tknize_sbuf, c);
  495.             AdvChar();
  496.             return pp_number();
  497.             }
  498.          else if (c == '.' && next_char[1] == '.') {
  499.             /*
  500.              *  ...
  501.              */
  502.             AdvChar();
  503.             AdvChar();
  504.             AppChar(tknize_sbuf, '.');
  505.             AppChar(tknize_sbuf, '.');
  506.             return new_token(Elipsis, str_install(&tknize_sbuf), fname, line);
  507.             }
  508.          else
  509.             return new_token('.', str_install(&tknize_sbuf), fname, line);
  510.  
  511.       case '+':
  512.          c = *next_char;
  513.          if (c == '+') {
  514.             /*
  515.              *  ++
  516.              */
  517.             AppChar(tknize_sbuf, '+');
  518.             AdvChar();
  519.             return new_token(Incr, str_install(&tknize_sbuf), fname, line);
  520.             }
  521.          else if (c == '=') {
  522.             /*
  523.              *  +=
  524.              */
  525.             AppChar(tknize_sbuf, '=');
  526.             AdvChar();
  527.             return new_token(PlusAsgn, str_install(&tknize_sbuf), fname, line);
  528.             }
  529.          else
  530.             return new_token('+', str_install(&tknize_sbuf), fname, line);
  531.  
  532.       case '-':
  533.          c = *next_char;
  534.          if (c == '>') {
  535.             /*
  536.              *  ->
  537.              */
  538.             AppChar(tknize_sbuf, '>');
  539.             AdvChar();
  540.             return new_token(Arrow, str_install(&tknize_sbuf), fname, line);
  541.             }
  542.          else if (c == '-') {
  543.             /*
  544.              *  --
  545.              */
  546.             AppChar(tknize_sbuf, '-');
  547.             AdvChar();
  548.             return new_token(Decr, str_install(&tknize_sbuf), fname, line);
  549.             }
  550.          else if (c == '=') {
  551.             /*
  552.              *  -=
  553.              */
  554.             AppChar(tknize_sbuf, '=');
  555.             AdvChar();
  556.             return new_token(MinusAsgn, str_install(&tknize_sbuf), fname,
  557.                line);
  558.             }
  559.          else
  560.             return new_token('-', str_install(&tknize_sbuf), fname, line);
  561.  
  562.       case '<':
  563.          c = *next_char;
  564.          if (c == '<') {
  565.             AppChar(tknize_sbuf, '<');
  566.             AdvChar();
  567.             if (*next_char == '=') {
  568.                /*
  569.                 *  <<=
  570.                 */
  571.                AppChar(tknize_sbuf, '=');
  572.                AdvChar();
  573.                return new_token(LShftAsgn, str_install(&tknize_sbuf), fname,
  574.                   line);
  575.                }
  576.             else
  577.                /*
  578.                 *  <<
  579.                 */
  580.                return new_token(LShft, str_install(&tknize_sbuf), fname, line);
  581.             }
  582.          else if (c == '=') {
  583.             /*
  584.              *  <=
  585.              */
  586.             AppChar(tknize_sbuf, '=');
  587.             AdvChar();
  588.             return new_token(Leq, str_install(&tknize_sbuf), fname, line);
  589.             }
  590.          else
  591.             return new_token('<', str_install(&tknize_sbuf), fname, line);
  592.  
  593.       case '>':
  594.          c = *next_char;
  595.          if (c == '>') {
  596.             AppChar(tknize_sbuf, '>');
  597.             AdvChar();
  598.             if (*next_char == '=') {
  599.                /*
  600.                 *  >>=
  601.                 */
  602.                AppChar(tknize_sbuf, '=');
  603.                AdvChar();
  604.                return new_token(RShftAsgn, str_install(&tknize_sbuf), fname,
  605.                   line);
  606.                }
  607.             else
  608.                /*
  609.                 *  >>
  610.                 */
  611.                return new_token(RShft, str_install(&tknize_sbuf), fname, line);
  612.             }
  613.          else if (c == '=') {
  614.             /*
  615.              *  >=
  616.              */
  617.             AppChar(tknize_sbuf, '=');
  618.             AdvChar();
  619.             return new_token(Geq, str_install(&tknize_sbuf), fname, line);
  620.             }
  621.          else
  622.             return new_token('>', str_install(&tknize_sbuf), fname, line);
  623.  
  624.       case '=':
  625.          if (*next_char == '=') {
  626.             /*
  627.              *  ==
  628.              */
  629.             AppChar(tknize_sbuf, '=');
  630.             AdvChar();
  631.             return new_token(Equal, str_install(&tknize_sbuf), fname, line);
  632.             }
  633.          else
  634.             return new_token('=', str_install(&tknize_sbuf), fname, line);
  635.  
  636.       case '!':
  637.          if (*next_char == '=') {
  638.             /*
  639.              *  !=
  640.              */
  641.             AppChar(tknize_sbuf, '=');
  642.             AdvChar();
  643.             return new_token(Neq, str_install(&tknize_sbuf), fname, line);
  644.             }
  645.          else
  646.             return new_token('!', str_install(&tknize_sbuf), fname, line);
  647.  
  648.       case '&':
  649.          c = *next_char;
  650.          if (c == '&') {
  651.             /*
  652.              *  &&
  653.              */
  654.             AppChar(tknize_sbuf, '&');
  655.             AdvChar();
  656.             return new_token(And, str_install(&tknize_sbuf), fname, line);
  657.             }
  658.          else if (c == '=') {
  659.             /*
  660.              *  &=
  661.              */
  662.             AppChar(tknize_sbuf, '=');
  663.             AdvChar();
  664.             return new_token(AndAsgn, str_install(&tknize_sbuf), fname, line);
  665.             }
  666.          else
  667.             return new_token('&', str_install(&tknize_sbuf), fname, line);
  668.  
  669.       case '|':
  670.          c = *next_char;
  671.          if (c == '|') {
  672.             /*
  673.              *  ||
  674.              */
  675.             AppChar(tknize_sbuf, '|');
  676.             AdvChar();
  677.             return new_token(Or, str_install(&tknize_sbuf), fname, line);
  678.             }
  679.          else if (c == '=') {
  680.             /*
  681.              *  |=
  682.              */
  683.             AppChar(tknize_sbuf, '=');
  684.             AdvChar();
  685.             return new_token(OrAsgn, str_install(&tknize_sbuf), fname, line);
  686.             }
  687.          else
  688.             return new_token('|', str_install(&tknize_sbuf), fname, line);
  689.  
  690.       case '*':
  691.          if (*next_char == '=') {
  692.             /*
  693.              *  *=
  694.              */
  695.             AppChar(tknize_sbuf, '=');
  696.             AdvChar();
  697.             return new_token(MultAsgn, str_install(&tknize_sbuf), fname, line);
  698.             }
  699.          else
  700.             return new_token('*', str_install(&tknize_sbuf), fname, line);
  701.  
  702.       case '/':
  703.          if (*next_char == '=') {
  704.             /*
  705.              *  /=
  706.              */
  707.             AppChar(tknize_sbuf, '=');
  708.             AdvChar();
  709.             return new_token(DivAsgn, str_install(&tknize_sbuf), fname, line);
  710.             }
  711.          else
  712.             return new_token('/', str_install(&tknize_sbuf), fname, line);
  713.  
  714.       case '%':
  715.          if (*next_char == '=') {
  716.             /*
  717.              *  &=
  718.              */
  719.             AppChar(tknize_sbuf, '=');
  720.             AdvChar();
  721.             return new_token(ModAsgn, str_install(&tknize_sbuf), fname, line);
  722.             }
  723.          else
  724.             return new_token('%', str_install(&tknize_sbuf), fname, line);
  725.  
  726.       case '^':
  727.          if (*next_char == '=') {
  728.             /*
  729.              *  ^=
  730.              */
  731.             AppChar(tknize_sbuf, '=');
  732.             AdvChar();
  733.             return new_token(XorAsgn, str_install(&tknize_sbuf), fname, line);
  734.             }
  735.          else
  736.             return new_token('^', str_install(&tknize_sbuf), fname, line);
  737.  
  738.       case '#':
  739.          /*
  740.           * Token pasting or stringizing operator.
  741.           */
  742.          if (*next_char == '#') {
  743.             /*
  744.              *  ##
  745.              */
  746.             AppChar(tknize_sbuf, '#');
  747.             AdvChar();
  748.             t1 =  new_token(PpPaste, str_install(&tknize_sbuf), fname, line);
  749.             }
  750.          else
  751.             t1 = new_token('#', str_install(&tknize_sbuf), fname, line);
  752.  
  753.          /*
  754.           * The operand must be in the same preprocessing directive.
  755.           */
  756.          if ((t2 = chck_wh_sp(cs)) != NULL)
  757.             if (t2->tok_id == PpDirEnd)
  758.               errt2(t2, t1->image,
  759.                " preprocessing expression must not cross directive boundry");
  760.             else
  761.                free_t(t2);
  762.          return t1;
  763.  
  764.       default:
  765.          return new_token(c, str_install(&tknize_sbuf), fname, line);
  766.       }
  767.    }
  768.